
package com.example.springcloudeurekaserverproducer.mybatis.interceptor;

import com.example.springcloudeurekaserverproducer.mybatis.annotation.*;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.assertj.core.util.Lists;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 
 * @ClassName: SqlBuilder
 * @Description: TODO(构建sql)
 * @author lanyuansong
 * @date 2018年7月11日
 */
public class SqlBuilder {

	private static final List<String> PROPERTY_DESCRIPTOR_LIST = Lists.newArrayList("class","annotatedInterfaces","annotatedSuperclass","annotation","annotations");
	private static final List<Class> CLASS_LIST = Lists.newArrayList(long.class,Long.class,int.class,Integer.class,Date.class,Timestamp.class);
	
	/**
	 * 
	 * @Title: getTableName 
	 * @Description: 获取表名称 
	 * @param @param clazz
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	private static String getTableName(Class<?> clazz) {
		TableMapperAnnotation tableMapperAnnotation = clazz
				.getAnnotation(TableMapperAnnotation.class);
		return tableMapperAnnotation.tableName();
	}
	
	/**
	 * 获取TableMapperAnnotation注解信息
	* @Title: gettableMapperAnnotation
	* @author lanyuansong
	* @Description: TODO(这里用一句话描述这个方法的作用)
	* @param @param clazz
	* @param @return    参数
	* @return TableMapperAnnotation    返回类型
	* @throws
	 */
	private static TableMapperAnnotation gettableMapperAnnotation(Class<?> clazz) {
		TableMapperAnnotation tableMapperAnnotation = clazz
				.getAnnotation(TableMapperAnnotation.class);
		return tableMapperAnnotation;
	}
	
	/**
	 * 获取Sequence 名称
	* @Title: getSequenceGenerator
	* @author lanyuansong
	* @Description: TODO(这里用一句话描述这个方法的作用)
	* @param @param clazz
	* @param @param name
	* @param @return    参数
	* @return String    返回类型
	* @throws
	 */
	private static String getSequenceGenerator(Class<?> clazz,String name) {
		SequenceGenerator sequencegenerator = null;
		try {
			sequencegenerator = clazz.getDeclaredField(name).getAnnotation(SequenceGenerator.class);
			if(null != sequencegenerator)
				return sequencegenerator.sequenceName();
			else
			    return null;
			
		} catch (NoSuchFieldException | SecurityException e) {
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * 获取uuid注解
	* @Title: getUUIDGenerator
	* @author lanyuansong
	* @Description: TODO(这里用一句话描述这个方法的作用)
	* @param @param clazz
	* @param @param name
	* @param @return    参数
	* @return UUIDGenerator    返回类型
	* @throws
	 */
	private static UUIDGenerator getUUIDGenerator(Class<?> clazz, String name) {
		UUIDGenerator uuidgenerator = null;
		try {
			uuidgenerator = clazz.getDeclaredField(name).getAnnotation(UUIDGenerator.class);
			return uuidgenerator;
		} catch (NoSuchFieldException | SecurityException e) {
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * 返回version的下一个值
	* @Title: getUpdatedVersionVal
	* @author lanyuansong
	* @Description: TODO(这里用一句话描述这个方法的作用)
	* @param @param originalVersionVal
	* @param @return    参数
	* @return Object    返回类型
	* @throws
	 */
    protected static Object getUpdatedVersionVal(Object originalVersionVal) {
        Class<?> versionValClass = originalVersionVal.getClass();
        if (long.class.equals(versionValClass)) {
            return ((long) originalVersionVal) + 1;
        } else if (Long.class.equals(versionValClass)) {
            return ((Long) originalVersionVal) + 1;
        } else if (int.class.equals(versionValClass)) {
            return ((int) originalVersionVal) + 1;
        } else if (Integer.class.equals(versionValClass)) {
            return ((Integer) originalVersionVal) + 1;
        } else if (Date.class.equals(versionValClass)) {
            return new Date();
        } else if (Timestamp.class.equals(versionValClass)) {
            return new Timestamp(System.currentTimeMillis());
        }
        return originalVersionVal;//？？？抛异常还是？？？
    }

	/**
	 * 
	* @Title: getVersion
	* @author lanyuansong
	* @Description: TODO(这里用一句话描述这个方法的作用)
	* @param @param clazz
	* @param @param name
	* @param @return    参数
	* @return Version    返回类型
	* @throws
	 */
	private static Version getVersion(Class<?> clazz, String name) {
		Version version = null;
		try {
			Field field= clazz.getDeclaredField(name);
			version = field.getAnnotation(Version.class);
			if(version==null)
				return version;
			 if(!CLASS_LIST.contains(field.getType())){
				 return null ;
			 } 
				return version;
		} catch (NoSuchFieldException | SecurityException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 
	 * @Title: getFiledName 
	 * @Description: 获取字段属性上的属性
	 * @param @param clazz
	 * @param @param propertyName
	 * @param @return    设定文件 
	 * @return FiledMappingAnnotation    返回类型 
	 * @throws
	 */
	private static FiledMappingAnnotation getFiledName(Class<?> clazz, String propertyName){
		 try {
			// System.out.println("propertyName:"+propertyName);
			 return clazz.getDeclaredField(propertyName).getAnnotation(FiledMappingAnnotation.class);
		} catch (NoSuchFieldException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 return null;
	}

	/**
	 * 
	 * @Title: builderInsert 
	 * @param @param object
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	private static <T>List<SqlProperty> builderSqlProperty(T t) {
		List<SqlProperty> sqlList = Lists.newArrayList();
		Class<?> clazz = t.getClass();
	
		// 获取字段上的注解
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			SqlProperty sqlproperty = null;
			for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
				String disPlayNanme = propertyDescriptor.getName();
				//System.out.println("------------------"+disPlayNanme);
				 if(PROPERTY_DESCRIPTOR_LIST.contains(disPlayNanme.trim())){
					 continue;
				 }                                 //annotatedInterfaces

				FiledMappingAnnotation filedMapping = SqlBuilder.getFiledName(clazz, disPlayNanme); 
				String dbFieldName =  filedMapping.dbFieldName(); 
				JdbcType jdbcType = filedMapping.jdbcType();
				//System.out.println("dbFieldName:"+dbFieldName);
				//System.out.println("jdbcType:"+jdbcType.name());
				String sequenceName =  getSequenceGenerator(clazz,disPlayNanme);
				UUIDGenerator uuid = getUUIDGenerator(clazz,disPlayNanme);
				Version version=   getVersion(clazz, disPlayNanme);
//				Method method =  propertyDescriptor.getReadMethod();
//				Object [] args = null;
//				Object returnValue = method.invoke(object, args);
				sqlproperty = new SqlProperty(dbFieldName,jdbcType,sequenceName,disPlayNanme);
				sqlproperty.setUuid(uuid);
				sqlproperty.setVersion(version);
				sqlList.add(sqlproperty);
				//System.out.println(returnValue);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sqlList;
	}
	
	/**
	 * 
	 * @Title: builderInsert 
	 * @Description: @Description: insert into BANK_DETAIL (BANK_DETAIL_ID, ORG_CODE, REMARK,RESV_FLD1,RESV_FLD2,ORG_NAME,BANK_DETAIL)
		values (SEQ_BANCK.nextval, #{orgCode,jdbcType=VARCHAR},#{remark,jdbcType=VARCHAR},#{resvFld1,jdbcType=VARCHAR},#{resvFld2,jdbcType=VARCHAR},#{orgName,jdbcType=VARCHAR},#{bankDetail,jdbcType=VARCHAR}) 
	 * @param @param object
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	public static <T>String builderInsert(T t){
		Class<?> clazz = t.getClass();
		TableMapperAnnotation tablemapperannotation = gettableMapperAnnotation(clazz);
		DBType dbType = tablemapperannotation.dbType();
		List<SqlProperty> list = builderSqlProperty(t);
		String tbName = tablemapperannotation.tableName();
		String uniqueKey = tablemapperannotation.uniqueKey();
		//UniqueKeyType uniqueKeyType = tablemapperannotation.uniqueKeyType();
		StringBuffer sqlBuffer = new StringBuffer(1024);
		sqlBuffer.append("INSERT INTO ").append(tbName).append("(");
		if(list.isEmpty()){
			throw new NullPointerException("sql字段不能为空");
		}
		StringBuffer filedBuffer = new StringBuffer(1024);
		StringBuffer valuedBuffer = new StringBuffer(1024);
		for(SqlProperty sqlProperty : list){
			String dbFilenName = sqlProperty.getDbFieldName();
			JdbcType jdbcType = sqlProperty.getJdbcType();
			//Object obejct =  sqlProperty.getObject();
			String sequenceName = sqlProperty.getSequenceName();
			String beanproperties = sqlProperty.getBeanproperties();
			//filedBuffer.append(dbFilenName).append(",");
			if(dbType.name().equals("ORACLE")){
				filedBuffer.append(dbFilenName).append(",");
				//如果sequenceName不为null这说明采用数据库字段
				if(!StringUtils.isBlank(sequenceName)){
					if(uniqueKey.equals(dbFilenName)){
						valuedBuffer.append(sequenceName+".NEXTVAL").append(",");
					}
				}else{
					valuedBuffer.append("#{"+beanproperties+",jdbcType="+jdbcType+"}").append(",");
				}
				
			}else if(dbType.name().equals("MYSQL") || dbType.name().equals("MSSQL")){
				if(StringUtils.isBlank(sequenceName) && !uniqueKey.equals(dbFilenName)){
					filedBuffer.append(dbFilenName).append(",");
					valuedBuffer.append("#{"+beanproperties+",jdbcType="+jdbcType+"}").append(",");
					
				}else if(uniqueKey.equals(dbFilenName) && !jdbcType.equals(JdbcType.INTEGER) ){
					filedBuffer.append(dbFilenName).append(",");
					valuedBuffer.append("#{"+beanproperties+",jdbcType="+jdbcType+"}").append(",");
				}
				
			}
			
			
		}
		String insertpart = filedBuffer.substring(0, filedBuffer.lastIndexOf(","));
		String valuepart = valuedBuffer.substring(0, valuedBuffer.lastIndexOf(","));
		String sql = sqlBuffer.append(insertpart).append(")").append("  VALUES(").append(valuepart).append(")").toString();
		return sql;
	}
	
	/**
	 * @throws NoSuchMethodException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * 
	 * @Title: builderUpdate 
	 * @Description:构建update sql语句 #{orgCode,jdbcType=VARCHAR}
	 * @param @param t
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	public static <T>String builderUpdate (T t) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		Class<?> clazz = t.getClass();
		TableMapperAnnotation tablemapperannotation = gettableMapperAnnotation(clazz);
		//DBType dbType = tablemapperannotation.dbType();
        Map dtoFieldMap = PropertyUtils.describe(t);//BeanUtils.describe(t);

		List<SqlProperty> list = builderSqlProperty(t);
		String tbName = tablemapperannotation.tableName();
		String uniqueKey = tablemapperannotation.uniqueKey();
		//UniqueKeyType uniqueKeyType = tablemapperannotation.uniqueKeyType();
		StringBuffer sqlBuffer = new StringBuffer(1024);
		sqlBuffer.append("UPDATE  ").append(tbName).append("  SET  ");
		if(list.isEmpty()){
			throw new NullPointerException("sql字段不能为空");
		}
		StringBuffer filedBuffer = new StringBuffer(1024);
		StringBuffer wheredBuffer = new StringBuffer(1024);
		for(SqlProperty sqlProperty : list){
			String dbFilenName = sqlProperty.getDbFieldName();
			JdbcType jdbcType = sqlProperty.getJdbcType();
			Version version =sqlProperty.getVersion();
			//Object obejct =  sqlProperty.getObject();
			//String sequenceName = sqlProperty.getSequenceName();
			String beanproperties = sqlProperty.getBeanproperties();
			if (version != null) {
				Object value = dtoFieldMap.get(beanproperties);
				if (value != null) {
					filedBuffer.append(dbFilenName + " =" + getUpdatedVersionVal(value)).append(",");
					wheredBuffer.append(dbFilenName + " = #{" + beanproperties + ",jdbcType=" + jdbcType + "}")
							.append("  AND  ");
				} else
					throw new NullPointerException(beanproperties + "字段不能为空");
			} else {
				if (uniqueKey.equals(dbFilenName)) {
					wheredBuffer.append(dbFilenName + " = #{" + beanproperties + ",jdbcType=" + jdbcType + "}")
							.append("  AND  ");
				} else {
					Object value = dtoFieldMap.get(beanproperties);
					if (value != null) {
						filedBuffer.append(dbFilenName + " = #{" + beanproperties + ",jdbcType=" + jdbcType + "}")
								.append(",");
					}
				}
			}
			
			
		}
		String insertpart = filedBuffer.substring(0, filedBuffer.lastIndexOf(","));
		String valuepart = wheredBuffer.substring(0, wheredBuffer.lastIndexOf("AND"));
		String sql = sqlBuffer.append(insertpart).append("  WHERE ").append(valuepart).toString();
		return sql;
	}
	
	
	/**
	 * 
	 * @Title: builderUpdate 
	 * @Description:构建update sql语句 #{orgCode,jdbcType=VARCHAR}
	 * @param @param t
	 * @param @return    设定文件 
	 * @return String    返回类型 
	 * @throws
	 */
	public static <T>String builderDelete(T t){
		Class<?> clazz = t.getClass();
		TableMapperAnnotation tablemapperannotation = gettableMapperAnnotation(clazz);
		//DBType dbType = tablemapperannotation.dbType();
		List<SqlProperty> list = builderSqlProperty(t);
		String tbName = tablemapperannotation.tableName();
		String uniqueKey = tablemapperannotation.uniqueKey();
		//UniqueKeyType uniqueKeyType = tablemapperannotation.uniqueKeyType();
		StringBuffer sqlBuffer = new StringBuffer(1024);
		sqlBuffer.append("DELETE  FROM   ").append(tbName).append("  WHERE  ");
		if(list.isEmpty()){
			throw new NullPointerException("sql字段不能为空");
		}
		StringBuffer wheredBuffer = new StringBuffer(1024);
		for(SqlProperty sqlProperty : list){
			String dbFilenName = sqlProperty.getDbFieldName();
			JdbcType jdbcType = sqlProperty.getJdbcType();
			//Object obejct =  sqlProperty.getObject();
			//String sequenceName = sqlProperty.getSequenceName();
			String beanproperties = sqlProperty.getBeanproperties();
			
			if(uniqueKey.equals(dbFilenName)){
				wheredBuffer.append(dbFilenName+" = #{"+beanproperties+",jdbcType="+jdbcType+"}").append("  AND  ");
			}
			
			
			
		}
		
		String valuepart = wheredBuffer.substring(0, wheredBuffer.lastIndexOf("AND"));
		String sql = sqlBuffer.append(valuepart).toString();
		return sql;
	}
	

    /**
     * 由传入的对象生成query sql语句
     * @param <T>
     * 
     * @param t
     * @return sql
     * @throws Exception
     */
    public static <T> String buildSelectSql(T t) throws Exception {
    	Class<?> clazz = t.getClass();
        Map dtoFieldMap = PropertyUtils.describe(t);//BeanUtils.describe(t);
		TableMapperAnnotation tablemapperannotation = gettableMapperAnnotation(clazz);
		//DBType dbType = tablemapperannotation.dbType();
		List<SqlProperty> list = builderSqlProperty(t);
		String tbName = tablemapperannotation.tableName();
//		String uniqueKey = tablemapperannotation.uniqueKey();
		//UniqueKeyType uniqueKeyType = tablemapperannotation.uniqueKeyType();
		StringBuffer sqlBuffer = new StringBuffer(1024);
		sqlBuffer.append("select ");
		if(list.isEmpty()){
			throw new NullPointerException("sql字段不能为空");
		}
		StringBuffer filedBuffer = new StringBuffer(1024);
		StringBuffer wheredBuffer = new StringBuffer(1024);
		for(SqlProperty sqlProperty : list){
			String dbFilenName = sqlProperty.getDbFieldName();
			JdbcType jdbcType = sqlProperty.getJdbcType();
			//Object obejct =  sqlProperty.getObject();
			//String sequenceName = sqlProperty.getSequenceName();
			String beanproperties = sqlProperty.getBeanproperties();
			filedBuffer.append(dbFilenName+" as "+beanproperties).append("  ,  ");
			  Object value = dtoFieldMap.get(beanproperties);
	            if (value != null&&!value.toString().trim().equals("0")) {
	    			wheredBuffer.append(" and ").append(dbFilenName+" = #{"+beanproperties+",jdbcType="+jdbcType+"}");
	            }
		}
		String selectpart = filedBuffer.substring(0, filedBuffer.lastIndexOf(","));
//		String valuepart = wheredBuffer.substring(0, wheredBuffer.lastIndexOf("AND"));
		String sql = sqlBuffer.append(selectpart).append("  from  ").append(tbName).append("  where 1=1  ").append(wheredBuffer).toString();
		return sql;
}
    /**
     * 由传入的对象生成query sql语句 根据id查询
     * @param <T>
     * 
     * @param t
     * @return sql
     * @throws Exception
     */
    public static <T> String buildSelectSqlById(T t) throws Exception {
    	Class<?> clazz = t.getClass();
//        Map dtoFieldMap = PropertyUtils.describe(t);
		TableMapperAnnotation tablemapperannotation = gettableMapperAnnotation(clazz);
		//DBType dbType = tablemapperannotation.dbType();
		List<SqlProperty> list = builderSqlProperty(t);
		String tbName = tablemapperannotation.tableName();
		String uniqueKey = tablemapperannotation.uniqueKey();
//		String uniqueKey = tablemapperannotation.uniqueKey();
		//UniqueKeyType uniqueKeyType = tablemapperannotation.uniqueKeyType();
		StringBuffer sqlBuffer = new StringBuffer(1024);
		sqlBuffer.append(" select ");
		if(list.isEmpty()){
			throw new NullPointerException("sql字段不能为空");
		}
		StringBuffer filedBuffer = new StringBuffer(1024);
		StringBuffer wheredBuffer = new StringBuffer(1024);
		for(SqlProperty sqlProperty : list){
			String dbFilenName = sqlProperty.getDbFieldName();
			JdbcType jdbcType = sqlProperty.getJdbcType();
			//Object obejct =  sqlProperty.getObject();
			//String sequenceName = sqlProperty.getSequenceName();
			String beanproperties = sqlProperty.getBeanproperties();
			filedBuffer.append(dbFilenName+" as "+beanproperties).append("  ,  ");
//			  Object value = dtoFieldMap.get(beanproperties);
				if(uniqueKey.equals(dbFilenName)){
					wheredBuffer.append(" and ").append(dbFilenName+" = #{"+beanproperties+",jdbcType="+jdbcType+"}");
				}
//	            if (value != null) {
//	    			wheredBuffer.append(" and ").append(dbFilenName+" = #{"+beanproperties+",jdbcType="+jdbcType+"}");
//	            }
		}
		String selectpart = filedBuffer.substring(0, filedBuffer.lastIndexOf(","));
//		String valuepart = wheredBuffer.substring(0, wheredBuffer.lastIndexOf("AND"));
		String sql = sqlBuffer.append(selectpart).append("  from  ").append(tbName).append("  where 1=1  ").append(wheredBuffer).toString();
		return sql;
}
}
